package api;

import infrastructure.LoggingManager;
import infrastructure.Utils;

import java.io.BufferedReader;
import java.io.FileReader;

import org.apache.log4j.Logger;

import classification.ProteinPredictionResult;


import weka.core.Instances;
import weka.core.SerializationHelper;
import weka.classifiers.Classifier;
import weka.classifiers.Evaluation;
import weka.classifiers.functions.MultilayerPerceptron;

public class WekaConnector {
	
	Classifier classifier;
	
	private Logger logger = LoggingManager.getLogger();
	
	private Instances trainingData;
	private Instances testingData;
	private AttributeFiltering attributeFiletering;
	
	public WekaConnector(String pathTrainingData, Object[] params, String pathTestingData) throws Exception {
		this.commonInitializer(pathTrainingData, pathTestingData);
		this.buildClassifierFromData(params);
	}
	
	public WekaConnector(String pathTrainingData, String pathModel, String pathTestingData) throws Exception {
		this.commonInitializer(pathTrainingData, pathTestingData);
		this.classifier = this.readClassifier(pathModel);		
		
	}
	
	private void commonInitializer(String pathTrainingData, String pathTestingData){
		this.trainingData = this.readData(pathTrainingData);
		this.attributeFiletering= new AttributeFiltering(pathTestingData);
	}
		
	public int getInstancesCountTraining(){
		return trainingData.numInstances();
	}
	
	public int getInstancesCountTesting(){
		return testingData.numInstances();
	}
	
	private Instances readData(String fileName) {
		Instances data = null;
		try {
			BufferedReader reader = new BufferedReader(new FileReader(fileName));
			data = new Instances(reader);
			reader.close();
			// setting class attribute
			data.setClassIndex(data.numAttributes() - 1);			
		} catch (Exception ex) {
			ex.printStackTrace();
		}		
		
		return data;
	}
	
	// BEGIN: Testing Methods to Create Classifier
	private void buildClassifierFromData(Object[] params) throws Exception{		
		this.classifier = this.createNeuralNetwork((Double)params[0], (Double)params[1], (Integer)params[2], (String)params[3]);
		logger.info("Building classifier...");
		classifier.buildClassifier(this.trainingData);
	}
	
	private MultilayerPerceptron createNeuralNetwork(Double momentum, Double learningRate, Integer trainingtime, String hiddenLayers){
		MultilayerPerceptron cls = new MultilayerPerceptron();
		cls.setMomentum(momentum);
		cls.setLearningRate(learningRate);
		cls.setTrainingTime(trainingtime);
		cls.setHiddenLayers(hiddenLayers);
		return cls;
	}
	// END: Testing Methods to Create Classifier
	
	private Classifier readClassifier(String pathModel) throws Exception{
		return (Classifier) SerializationHelper.read(pathModel);
	}
	
	public ProteinPredictionResult classify() throws Exception{		
		//this.testingData = this.readData(pathTestingData);
		logger.info("Parsing Input Data...");
		FilteredResult filterData = null;
		try { 
			filterData = attributeFiletering.getFilteredData();
			this.testingData = filterData.getFilteredData();
		} catch (Exception e) {
			logger.error(e);
			return null;
		}
		
		logger.info("Creating Evaluation Model");
		Evaluation eval = new Evaluation(this.trainingData);
		
		logger.info("Evaluating model " + Utils.concatenateString(classifier.getOptions()) );
		double [] output = eval.evaluateModel(classifier, this.testingData);
		logger.info(String.format("Samples classified: %d", output.length));
		
		ProteinPredictionResult result = new ProteinPredictionResult(eval.toSummaryString("\nResults\n======\n", false), output, filterData.getIndexesResult());
		return result;		
	}

}
